package cz.mff.cuni.dpp.liboptions;

import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;

/**
 * Podtřída {@link Option Option} pro volby s parametrem typu řetězce
 * vybraného z předem dané množiny.
 */
public class EnumOption extends Option<String> {

	private final Set<String> allowedValues = new HashSet<String>();

	private final boolean caseSensitive;

	protected EnumOption(String primaryName, Set<String> aliases, boolean obligatory,
			String valuePlaceholder, String description, OptionCallback<String> callback,
			Set<String> allowedValues, boolean caseSensitive) {
		super(primaryName, aliases, obligatory, valuePlaceholder, description, callback);

		for (String allowedValue : allowedValues) {
			this.allowedValues.add(allowedValue);
		}

		this.caseSensitive = caseSensitive;

	}

	protected EnumOption(Builder builder) {
		super(builder);

		for (String allowedValue : builder.getAllowedValues()) {
			this.allowedValues.add(allowedValue);
		}

		this.caseSensitive = builder.isCaseSensitive();
	}

	public boolean supportsValue() {
		return true;
	}

	public String parseValue(String value) throws BadValueException {
		if (isValueAllowed(value)) {
			return value;
		} else {
			throw new BadValueException(value, this, true);
		}
	}

    /**
     * @return množinu všech povolených hodnot
     */
	public Set<String> getAllowedValues() {
		return allowedValues;
	}

	public boolean isCaseSensitive() {
		return caseSensitive;
	}

	private boolean isValueAllowed(String value) {
		if (caseSensitive) {
			return containsCaseSensitive(value);
		} else {
			return containsIgnoreCase(value);
		}
	}

	private boolean containsCaseSensitive(String value) {
		if (allowedValues.contains(value)) {
			return true;
		}
		return false;
	}

	private boolean containsIgnoreCase(String value) {
		for (String allowedValue : allowedValues) {
			if (allowedValue.toLowerCase().equals(value.toLowerCase())) {
				return true;
			}
		}
		return false;
	}

    /**
     * Builder pro objekty typu {@link EnumOption}, potomek {@link
     * Option.Builder}
     */
	public static class Builder extends Option.Builder<String, Builder> {

		private Set<String> allowedValues = new HashSet<String>();

		private boolean caseSensitive = false;

		public Builder(String primaryName, String valuePlaceholder, boolean obligatory) {
			super(primaryName, valuePlaceholder, obligatory);
		}

		public Builder(String primaryName, String valuePlaceholder) {
			super(primaryName, valuePlaceholder);
		}

		@Override
		public EnumOption buildOption() {
			return new EnumOption(this);
		}

		public Set<String> getAllowedValues() {
			if (allowedValues == null) {
				allowedValues = new HashSet<String>();
			}
			return allowedValues;
		}

		public Builder setAllowedValues(Set<String> allowedValues) {
			this.allowedValues = allowedValues;
			return this;
		}

		public Builder addAllowedValues(String... allowedValues) {
			getAllowedValues().addAll(Arrays.asList(allowedValues));
			return this;
		}

		public boolean isCaseSensitive() {
			return caseSensitive;
		}

		public Builder setCaseSensitive(boolean caseSensitive) {
			this.caseSensitive = caseSensitive;
			return this;
		}

	}

}
